package kqbuffer

import (
	"context"
	"errors"
)

var (
	ErrNotPoolBuffer      = errors.New("not kqPoolBuffer")
	ErrInvalidBufferToken = errors.New("invalid KqBufferPool token")
	ErrCtxDone            = errors.New("context.Context Done")
)

type (
	KqBufferToken = int

	kqPoolBuffer struct {
		*kqBuffer
		token KqBufferToken
	}
)

func (pb *kqPoolBuffer) AsRead() KqReadBuffer {
	pb.kqBuffer.AsRead()
	return pb
}

/*
 * buffer pool
 */
type KqBufferPool struct {
	bufferPool  []kqPoolBuffer
	poolSize    int
	buffserSize int

	freeBufChan chan KqBufferToken // 空闲缓冲区 token 列表
}

func NewKqBufferPool(poolSize int, buffserSize int) *KqBufferPool {
	freeBufChan := make(chan KqBufferToken, poolSize)

	bufferPool := make([]kqPoolBuffer, 0, poolSize)
	for i := 0; i < poolSize; i++ {
		bufferPool = append(bufferPool, kqPoolBuffer{
			kqBuffer: newKqBuffer(buffserSize),
			token:    i,
		})
		freeBufChan <- i
	}

	return &KqBufferPool{
		bufferPool:  bufferPool,
		poolSize:    poolSize,
		buffserSize: buffserSize,

		freeBufChan: freeBufChan,
	}
}

/*
 * 获取空 write buffer。若不存在，阻塞直到返回
 */
func (p *KqBufferPool) GetEmptyWriteBuffer(ctx context.Context) (KqWriteBuffer, error) {
	select {
	case <-ctx.Done():
		return nil, ErrCtxDone
	case token := <-p.freeBufChan:
		return &p.bufferPool[token], nil
	}
}

/*
 * 获取空 write buffer。非阻塞版本，bool返回值表示结果
 */
func (p *KqBufferPool) TryGetEmptyWriteBuffer() (KqWriteBuffer, bool) {
	select {
	case token := <-p.freeBufChan:
		return &p.bufferPool[token], true
	default:
		return nil, false
	}
}

/*
 * 放回 read buffer，返回结果
 */
func (p *KqBufferPool) PushBackReadBuffer(buffer KqReadBuffer) error {
	pBuffer, ok := buffer.(*kqPoolBuffer)
	if !ok {
		return ErrNotPoolBuffer
	}
	if pBuffer.token < 0 || pBuffer.token >= p.poolSize {
		return ErrInvalidBufferToken
	}
	err := pBuffer.ResetBuffer()
	if err != nil {
		return err
	}
	p.freeBufChan <- pBuffer.token
	return nil
}
